#! /usr/bin/env python3
"""
Doctor for checking

When the vm is not working.
"""
import subprocess
import time
from datetime import datetime
from ipaddress import ip_interface
import logging
import shlex
import getopt
import re
import select
import sys


LOG_FILE_NAME = 'dcn_doctor.log'

logging.basicConfig(level=logging.DEBUG,
    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
    datefmt='%a, %d %b %Y %H:%M:%S',
    filename= LOG_FILE_NAME,
    filemode='w')

console = logging.StreamHandler()
console.setLevel(logging.INFO)
#formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
formatter = logging.Formatter('%(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)


class DcnDoctor(object):
    """
    This class handles dcn doctor.
    """

    def __init__(self, controller_address, gateway_address, vm_address):
        self.controller_address = controller_address
        self.gateway_address = gateway_address
        self.vm_address = vm_address
        self.all_gateway_chassis = None

    def run_command(self, cmd):
        status = True
        result = ""
        try:
            p = subprocess.Popen(
                cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            raw_data, err = p.communicate()
            #logging.info('raw_data:%s', raw_data)
            if str(err,'utf-8') == '':
                result = str(raw_data,'utf-8').strip()
            #logging.info('result:%s', result)
        except Exception:
            status = False
        return status, result

    def get_sb_chassis_for_vm(self,vm_address):
        """
        Entry point for get_vtep_chassis
        ovsdb-client dump tcp:172.171.202.116:6642 Port_Binding chassis external_ids |grep 192.168.8.11
        """
        try:
            sb_chassis = None
            user_cmd = 'ovsdb-client dump tcp:'+ self.controller_address + ':6642 Port_Binding chassis external_ids' + ' | grep ' + vm_address
            status, result = self.run_command(user_cmd)
            if status:
                result = result.split(' ')
                user_cmd2 = 'ovsdb-client dump tcp:'+ self.controller_address + ':6642 Chassis _uuid encaps' + ' | grep ' + result[0]
                status2, result2 = self.run_command(user_cmd2)
                if status2:
                    result2 = result2.split(' ')
                    encap = result2[1]
                    sb_chassis = encap[1:8]
                    
                    user_cmd3 = 'ovsdb-client dump tcp:'+ self.controller_address + ':6642 Encap _uuid ip' + ' | grep ' + encap[1:8]
                    status3, result3 = self.run_command(user_cmd3)
                    if status3:
                        result3 = result3.split('\"')
                        ip = result3[1]
                        sb_chassis = ip
        except Exception:
            return None
        return sb_chassis
        
    def get_gateway_chassis_for_vm(self, vm_address):
        """
        Entry point for get_vtep_chassis
        """
        vtep_chassis = None
        if self.all_gateway_chassis == None:
            return vtep_chassis
        
        for value in self.all_gateway_chassis:
            result = value.split('\"')
            ip_pfix = result[1]
            nexthop = result[3]
            
            ip = ip_pfix.split('/')
            vm_gateway_address = ip[0]
            if vm_address == vm_gateway_address:
                vtep_chassis = nexthop
        return vtep_chassis

    def get_all_gateway_chassis(self):
        """
        Entry point for get_vtep_chassis
        """
        all_chassis = None
        user_cmd = 'ovsdb-client dump tcp:'+ self.gateway_address + ':6644 Route ip_prefix nexthop' + ' | grep -v ip_prefix |grep -v table '
        status, result = self.run_command(user_cmd)        
        if status:
            result = result.split('\n')
            #for value in result[1:]:
            #    logging.info('all chassis %s'%(value))
            all_chassis = result[1:]
            #logging.info('all chassis %s'%(vtep_chassis))
        return all_chassis

    def check_all_chassis_process(self):
        """
        Entry point for check_chassis_process
        """
        logging.info('Begin to check all the chassis on gateway ')
        vtep_chassis = None
        if self.all_gateway_chassis == None:
            logging.info('There is no chassis on gateway.')
            return
        
        for value in self.all_gateway_chassis:
            result = value.split('\"')
            ip_pfix = result[1]
            nexthop = result[3]

            ip = ip_pfix.split('/')
            vm_address = ip[0]
            #logging.info('ip %s ,vm_address %s'%(ip,vm_address))
            self.check_single_chassis_process(vm_address)
        return

    def check_single_chassis_process(self,vm_address):
        """
        Entry point for check_chassis_process
        """
        logging.info('Begin to check the chassis for vm %s '%(vm_address))
        gateway_chassis = self.get_gateway_chassis_for_vm(vm_address)
        sb_chassis = self.get_sb_chassis_for_vm(vm_address)
        logging.info('    The gateway_chassis :%s'%(gateway_chassis))
        logging.info('    The sb_chassis      :%s'%(sb_chassis))
        logging.info('----------------------------------------------------------------')

        if gateway_chassis == sb_chassis :
            logging.info('Check result: OK')
        else:
            if sb_chassis == None :
                #ovnsb port binding may have multi
                logging.info('Check result: OK')
            else:
                logging.info('Check result: Failed')
        logging.info('----------------------------------------------------------------')

    def check_ovsdb_client_install(self):
        """
        Entry point for the DcnDoctor class
        """
        user_cmd = 'ovsdb-client --help'
        status, result = self.run_command(user_cmd)
        if result == "":
            return False
        else:
            return True

    def run(self):
        """
        Entry point for the DcnDoctor class
        """
        self.all_gateway_chassis  = self.get_all_gateway_chassis()
        if self.vm_address :
            self.check_single_chassis_process(self.vm_address)
        else:
            self.check_all_chassis_process()

def print_dcn_doctor_window():
    """
    logging.info('  __ __    ___     ____  _ _ _    ___    _ _ _  ')
    logging.info(' | _   \  / _ \  / _ _\ |_ _ _|  / _ \  /  _  \ ')
    logging.info(' | | \ | | | | | | |      | |   | | | | | |_| / ')
    logging.info(' | |_/ | | |_| | | |_ _   | |   | |_| | | |\ \  ')
    logging.info(' |__  _/  \___/  \____/   |_|    \___/  |_| \_\ ')
    """
    logging.info('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
    logging.info('+++++++++++++++   WELCOM TO DCN DOCTOR    ++++++++++++++++++++++')
    logging.info('      __ __    ___    ____   _ _ _    ___    _ _ _  ')
    logging.info('     | _   \  / _ \  / _ _\ |_ _ _|  / _ \  /  _  \ ')
    logging.info('     | | \ | | | | | | |      | |   | | | | | |_| / ')
    logging.info('     | |_/ | | |_| | | |_ _   | |   | |_| | | |\ \  ')
    logging.info('     |__  _/  \___/  \____/   |_|    \___/  |_| \_\ ')
    logging.info('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')

def main(argv):
    controller_address = None
    gateway_address = None
    vm_address = None
    
    opts,args = getopt.getopt(argv,'-h-c:-g:-v:',['help','controller-address=','gateway-address=','vm-address='])
    #logging.info('opts :%s', opts)
    #logging.info('args :%s', args)
    for opt_name,opt_value in opts:
        if opt_name in ('-h','--help'):
            logging.info('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
            logging.info('                                 controller-address  gateway-address    vm-address')
            logging.info('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
            logging.info('Example1:    python3 doctor.py -c 172.171.202.116 -g 172.171.203.219')
            logging.info('Example2:    python3 doctor.py -c 172.171.202.116 -g 172.171.203.219 -v 192.168.8.11')
            logging.info('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
            sys.exit(1)
        if opt_name in ('-c','--controller-address'):
            controller_address = opt_value
        if opt_name in ('-g','--gateway-address'):
            gateway_address = opt_value
        if opt_name in ('-v','--vm-address'):
            vm_address = opt_value
    print_dcn_doctor_window()
    logging.info('Input controller_address :%s'% (controller_address))
    logging.info('Input gateway_address    :%s'% (gateway_address))
    logging.info('Input vm_address         :%s'% (vm_address))
    logging.info('++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++')
    if not controller_address:
        logging.info('controller address not specified. Exiting...')
        sys.exit(1)
    if not gateway_address:
        logging.info('gateway address not specified. Exiting...')
        sys.exit(1)
    """
    if not vm_address:
        logging.info('vm address not specified. Exiting...')
        sys.exit(1)
    """
    handler = DcnDoctor(controller_address,gateway_address,vm_address)
    if handler.check_ovsdb_client_install():
        handler.run()
    else:
        logging.info('Need to install ovsdb-client tool first. Exiting...')
        sys.exit(1)


if __name__ == "__main__":
    main(sys.argv[1:])
